/**
 * Copyright 2008-2009 HAN.BIFANG
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package han.bashe.common.dao;

import han.bashe.common.converter.InternalConverter;
import han.bashe.common.entity.BasheEntityConstants;
import han.bashe.common.entity.SoaMessageExchange;
import han.bashe.common.entity.SoaConversation;
import han.bashe.common.entity.SoaErrorMessage;
import han.bashe.common.entity.SoaMessageLog;
import han.bashe.common.entity.SoaRequestMessage;
import han.bashe.common.entity.SoaResponseMessage;
import han.bashe.common.entity.SoaRetryRecord;
import han.bashe.common.exception.BasheErrorCode;
import han.bashe.common.exception.BasheException;
import han.bashe.common.message.NormalizedMessage;
import han.bashe.common.message.RequestHelper;
import han.bashe.common.util.CommonUtils;
import han.bashe.common.util.ExceptionUtils;
import han.common.persist.dao.DaoManager;
import han.common.persist.tx.Tx;
import han.common.persist.tx.TxManager;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @Author Kent
 */
public class MessageDao {

	public static String createRequest(NormalizedMessage req)
			throws BasheException {
		Tx tx = null;
		try {
			tx = TxManager.start();
			SoaMessageLog log = new SoaMessageLog();
			fillMessageLog(req, log);
			log.setStatus(BasheEntityConstants.STATUS_INIT);

			SoaRequestMessage request = new SoaRequestMessage();
			try {
				System.out.println("##@@##" + req);
				request.setMessage(InternalConverter.normailzed2Bashe(req)
						.toString());

			} catch (Exception e1) {
				e1.printStackTrace();
				throw new BasheException(BasheErrorCode.PARSE_CLIENT_XML_ERROR,
						"parse client xml error, please check client body xml",e1);
			}

			DaoManager.getDao().save(log);
			request.setId(log.getId());
			DaoManager.getDao().save(request);

			DaoManager.getDao().flush();
			tx.commit();
			return log.getId();
		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.createRequest()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"createRequest() error", e);
		} finally {
			tx.end();
		}

	}

	public static void createResponse(String logId, NormalizedMessage res,
			long responseTime) throws BasheException {
		Tx tx = null;
		try {
			tx = TxManager.start();
			boolean create = false;
			SoaResponseMessage response = (SoaResponseMessage) DaoManager
					.getDao().get(SoaResponseMessage.class, logId);

			if (response == null) {
				response = new SoaResponseMessage();
				response.setId(logId);
				create = true;
			}

			try {

				response.setMessage(InternalConverter.normailzed2Bashe(res)
						.toString());

			} catch (Exception e1) {
				throw new BasheException(
						BasheErrorCode.PARSE_RESPONSE_XML_ERROR,
						"parse service response xml error, please check the service");
			}
			if (create)
				DaoManager.getDao().save(response);
			else
				DaoManager.getDao().merge(response);
			
			SoaMessageLog log = (SoaMessageLog) DaoManager.getDao().get(
					SoaMessageLog.class, logId);
			log.setStatus(BasheEntityConstants.STATUS_OK);
			log.setServiceResponseTime(String.valueOf(responseTime));
			DaoManager.getDao().update(log);

			DaoManager.getDao().flush();
			tx.commit();
		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.createResponse()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"createResponse() error", e);
		} finally {
			tx.end();
		}

	}

	public static String createError(SoaErrorMessage error)
			throws BasheException {
		Tx tx = null;
		try {
			tx = TxManager.start();

			DaoManager.getDao().save(error);
			DaoManager.getDao().flush();
			tx.commit();
			return error.getId();
		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.createError()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"createError() error", e);
		} finally {
			tx.end();
		}

	}

	public static String createRetryRecord(SoaRetryRecord retry)
			throws BasheException {
		Tx tx = null;
		try {
			tx = TxManager.start();

			DaoManager.getDao().save(retry);

			DaoManager.getDao().flush();
			tx.commit();
			return retry.getId();
		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.createRetryRecord()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"createRetryRecord() error", e);
		} finally {
			tx.end();
		}

	}

	public static void setRetryError(String retryId, String errorId)
			throws BasheException {
		Tx tx = null;
		try {
			tx = TxManager.start();

			SoaRetryRecord r = (SoaRetryRecord) DaoManager.getDao().get(
					SoaRetryRecord.class, retryId);
			if (r != null) {
				r.setErrorMessageId(errorId);
				DaoManager.getDao().merge(r);
			}

			DaoManager.getDao().flush();
			tx.commit();

		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.createRetryRecord()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"createRetryRecord() error", e);
		} finally {
			tx.end();
		}

	}

	public static String createConversation(SoaConversation con)
			throws BasheException {
		Tx tx = null;
		try {
			tx = TxManager.start();
			DaoManager.getDao().save(con);
			DaoManager.getDao().flush();
			tx.commit();
			return con.getId();
		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.createConversation()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"createConversation() error", e);
		} finally {
			tx.end();
		}

	}

	public static void modifyMessageLog(SoaMessageLog log)
			throws BasheException {
		Tx tx = null;
		try {
			tx = TxManager.start();
			DaoManager.getDao().merge(log);
			DaoManager.getDao().flush();
			tx.commit();

		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.modifyMessageLog()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"modifyMessageLog() error", e);
		} finally {
			tx.end();
		}

	}

	public static void modifyConversation(SoaConversation con)
			throws BasheException {
		Tx tx = null;
		try {
			tx = TxManager.start();
			DaoManager.getDao().merge(con);
			DaoManager.getDao().flush();
			tx.commit();

		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.modifyConversation()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"modifyConversation() error", e);
		} finally {
			tx.end();
		}

	}

	public static SoaMessageLog findMessageLog(String id) throws BasheException {
		Tx tx = null;
		try {
			SoaMessageLog ret = null;
			tx = TxManager.start();
			ret = (SoaMessageLog) DaoManager.getDao().get(SoaMessageLog.class,
					id);
			tx.commit();
			return ret;

		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.findMessageLog()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"findMessageLog() error", e);
		} finally {
			tx.end();
		}

	}

	public static SoaRequestMessage findRequest(String id)
			throws BasheException {
		Tx tx = null;
		try {
			SoaRequestMessage ret = null;
			tx = TxManager.start();
			ret = (SoaRequestMessage) DaoManager.getDao().get(
					SoaRequestMessage.class, id);
			tx.commit();
			return ret;

		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.findRequest()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"findRequest() error", e);
		} finally {
			tx.end();
		}

	}

	public static SoaResponseMessage findResponse(String id)
			throws BasheException {
		Tx tx = null;
		try {
			SoaResponseMessage ret = null;
			tx = TxManager.start();
			ret = (SoaResponseMessage) DaoManager.getDao().get(
					SoaResponseMessage.class, id);
			tx.commit();
			return ret;

		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.findResponse()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"findResponse() error", e);
		} finally {
			tx.end();
		}

	}

	public static SoaConversation findConversation(String id)
			throws BasheException {
		Tx tx = null;
		try {
			SoaConversation ret = null;
			tx = TxManager.start();
			ret = (SoaConversation) DaoManager.getDao().get(
					SoaConversation.class, id);
			tx.commit();
			return ret;

		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.findConversation()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"findConversation() error", e);
		} finally {
			tx.end();
		}

	}

	/**
	 * 
	 * @param logId
	 * @param req
	 * @param res
	 * @param be
	 * @return [0] return logId if creating new log [1] SoaErrorMessage id
	 * @throws BasheException
	 */
	public static String[] logError(String logId, NormalizedMessage req,
			NormalizedMessage res, BasheException be) throws BasheException {
		Tx tx = null;
		String[] ret = new String[2];
		try {
			tx = TxManager.start();
			if (logId == null) {
				SoaMessageLog log = new SoaMessageLog();
				fillMessageLog(req, log);
				log.setStatus(BasheEntityConstants.STATUS_ERROR);
				SoaRequestMessage request = new SoaRequestMessage();
				try {

					request.setMessage(InternalConverter.normailzed2Bashe(req)
							.toString());

				} catch (Exception e1) {
					throw new BasheException(
							BasheErrorCode.PARSE_CLIENT_XML_ERROR,
							"parse client xml error, please check client body xml");
				}
				DaoManager.getDao().save(log);
				request.setId(log.getId());
				DaoManager.getDao().save(request);

				if (res != null) {
					SoaResponseMessage response = new SoaResponseMessage();
					response.setId(log.getId());
					try {

						response.setMessage(InternalConverter.normailzed2Bashe(
								res).toString());

					} catch (Exception e1) {
						throw new BasheException(
								BasheErrorCode.PARSE_RESPONSE_XML_ERROR,
								"parse service response xml error, please check the service");
					}
					DaoManager.getDao().save(response);
				}
				SoaErrorMessage err = new SoaErrorMessage();
				err.setErrorCode(be.getCode());
				err.setErrorMsg(be.getMsg() + "\n"
						+ ExceptionUtils.getTraceAsString(be));
				err.setMessageLogId(log.getId());
				DaoManager.getDao().save(err);
				ret[0] = log.getId();
				ret[1] = err.getId();
			} else {
				SoaMessageLog log = (SoaMessageLog) DaoManager.getDao().get(
						SoaMessageLog.class, logId);
				log.setStatus(BasheEntityConstants.STATUS_ERROR);
				DaoManager.getDao().update(log);
				if (res != null) {
					SoaResponseMessage response = new SoaResponseMessage();
					response.setId(log.getId());
					try {

						response.setMessage(InternalConverter.normailzed2Bashe(
								res).toString());

					} catch (Exception e1) {
						throw new BasheException(
								BasheErrorCode.PARSE_RESPONSE_XML_ERROR,
								"parse service response xml error, please check the service");
					}
					DaoManager.getDao().saveOrUpdate(response);
				}
				SoaErrorMessage err = new SoaErrorMessage();
				err.setErrorCode(be.getCode());
				err.setErrorMsg(be.getMsg() + "\n"
						+ ExceptionUtils.getTraceAsString(be));
				err.setMessageLogId(log.getId());
				DaoManager.getDao().save(err);

				RequestHelper rh = new RequestHelper(req);
				if (rh.getConversationId() != null) {
					SoaConversation con = (SoaConversation) DaoManager.getDao()
							.get(SoaConversation.class, rh.getConversationId());
					if (con != null) {
						con.setStatus(BasheEntityConstants.STATUS_ERROR);
						DaoManager.getDao().update(con);
					}
				}

				ret[0] = logId;
				ret[1] = err.getId();
			}
			DaoManager.getDao().flush();

			tx.commit();
			return ret;
		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.findConversation()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"findConversation() error", e);
		} finally {
			tx.end();
		}

	}

	private static void fillMessageLog(NormalizedMessage req, SoaMessageLog log) {
		RequestHelper rh = new RequestHelper(req);
		log.setConversationId(rh.getConversationId());
		log.setLogTime(CommonUtils.getNowStr());
		log.setReplyServiceCode(rh.getReplyServiceCode());
		log.setReplyVersion(rh.getReplyServiceVersion());
		log.setRequestApplication(rh.getApplication());
		log.setRequestId(rh.getRequestId());
		log.setServiceCode(rh.getServiceCode());
		log.setVersion(rh.getServiceVersion());
		// log.setStatus(BasheEntityConstants.STATUS_INIT);
	}

	public static void setConversationId(String logId, String conId)
			throws BasheException {
		Tx tx = null;
		try {
			tx = TxManager.start();
			SoaMessageLog log = (SoaMessageLog) DaoManager.getDao().get(
					SoaMessageLog.class, logId);
			log.setConversationId(conId);
			DaoManager.getDao().update(log);
			DaoManager.getDao().flush();
			tx.commit();
		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.setConversationId()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"setConversationId() error", e);
		} finally {
			tx.end();
		}

	}

	public static SoaMessageExchange getMessageExchangeById(String id)
			throws BasheException {
		Tx tx = null;
		try {
			tx = TxManager.start();
			// String hql = "from SoaMessageLog m where m.requestId = ?";
			// List<SoaMessageLog> lgs =
			// DaoManager.getDao().find(hql,requestId);

			// if (lgs.size() == 0) {
			// tx.commit();
			// return null;
			//			
			// }
			SoaMessageLog m = (SoaMessageLog) DaoManager.getDao().get(
					SoaMessageLog.class, id);
			if (m == null) {
				tx.commit();
				return null;
			}

			SoaMessageExchange me = new SoaMessageExchange();
			me.setId(id);
			SoaRequestMessage request = (SoaRequestMessage) DaoManager.getDao()
					.get(SoaRequestMessage.class, m.getId());
			SoaResponseMessage response = (SoaResponseMessage) DaoManager
					.getDao().get(SoaResponseMessage.class, m.getId());
			String hql = "from SoaErrorMessage e where e.messageLogId = ?";
			List<SoaErrorMessage> errors = DaoManager.getDao().find(hql,
					m.getId());

			hql = "from SoaRetryRecord e where e.messageLogId = ?";
			List<SoaRetryRecord> res = DaoManager.getDao().find(hql, m.getId());

			me.setRequest(request);
			me.setResponse(response);
			me.setErrors(errors);
			me.setRetryRecords(res);

			tx.commit();
			return me;
		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.getMessageExchangeById()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"getMessageExchangeById() error", e);
		} finally {
			tx.end();
		}

	}

	public static List<SoaMessageExchange> getMessageExchangeByRequestId(
			String requestId) throws BasheException {
		Tx tx = null;
		try {
			tx = TxManager.start();
			String hql = "from SoaMessageLog m where m.requestId = ?";
			List<SoaMessageLog> lgs = DaoManager.getDao().find(hql, requestId);

			if (lgs.size() == 0) {
				tx.commit();
				return null;

			}
			List<SoaMessageExchange> mes = new ArrayList<SoaMessageExchange>();

			for (SoaMessageLog m : lgs) {

				SoaMessageExchange me = new SoaMessageExchange();
				me.setId(m.getId());
				SoaRequestMessage request = (SoaRequestMessage) DaoManager
						.getDao().get(SoaRequestMessage.class, m.getId());
				SoaResponseMessage response = (SoaResponseMessage) DaoManager
						.getDao().get(SoaResponseMessage.class, m.getId());
				hql = "from SoaErrorMessage e where e.messageLogId = ?";
				List<SoaErrorMessage> errors = DaoManager.getDao().find(hql,
						m.getId());

				hql = "from SoaRetryRecord e where e.messageLogId = ?";
				List<SoaRetryRecord> res = DaoManager.getDao().find(hql,
						m.getId());

				me.setRequest(request);
				me.setResponse(response);
				me.setErrors(errors);
				me.setRetryRecords(res);
				mes.add(me);
			}
			tx.commit();
			return mes;
		} catch (Throwable e) {
			Log logger = LogFactory.getLog(MessageDao.class);
			logger.error("MessageDao.getMessageExchangeById()", e);
			tx.rollback();
			throw new BasheException(BasheErrorCode.DB_ERROR,
					"getMessageExchangeById() error", e);
		} finally {
			tx.end();
		}

	}

}
